﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Linq.Expressions;
using System.Reflection;
using NFlickr.Core;
using NFlickr.Core.Search;
using NFlickr.Resource;

namespace WindowsFormsApplication1
{

    public class PhotoList : IQueryable<Photo>, IEnumerator<Photo>, IQueryProvider
    {

        public PhotoList(NFlickr.Core.FlickrService service)
        {
            _service = service;
        }
        private IList<Photo> _photos = new List<Photo>();
        private int _index = 0;


        public void Add(Photo photo)
        {
            _photos.Add(photo);
        }
        private NFlickr.Core.FlickrService _service;
        List<KeyValuePair<string, Func<int>>> functions = null;
        List<object> filterList = new List<object>();
        List<object> filterOperator = new List<object>();
        List<object> filterProperty = new List<object>();
        #region IEnumerable<Photo> Members

        public IEnumerator<Photo> GetEnumerator()
        {

            IEnumerator<Photo> list = (IEnumerator<Photo>)this;
            if (functions == null)
                return list;

            List<Photo> result = new List<Photo>();


            return result.GetEnumerator();

        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region IEnumerator<Photo> Members

        public Photo Current
        {
            get { return _photos.ElementAt(_index - 1); }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {

        }

        #endregion

        #region IEnumerator Members

        object IEnumerator.Current
        {
            get { return Current; }
        }

        public bool MoveNext()
        {
            _index++;
            if (_index > _photos.Count)
            {
                return false;
            }
            return true;
        }

        public void Reset()
        {
            _index = 0;
        }

        #endregion

        #region IQueryable Members

        public Type ElementType
        {
            get { return typeof(Photo); }
        }

        public System.Linq.Expressions.Expression Expression
        {
            get
            {
                return System.Linq.Expressions.Expression.Constant(this);
            }
        }

        public IQueryProvider Provider
        {
            get
            {
                return this;
            }
        }

        #endregion

        #region IQueryProvider Members

        public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        {
            IQueryable<TElement> element = null;
            switch (expression.NodeType)
            {
                case System.Linq.Expressions.ExpressionType.Call:
                    MethodCallExpression callExpression = (MethodCallExpression)expression;
                    switch (callExpression.Method.Name)
                    {
                        case "Select":
                            element = new StudentQueryObject<TElement>(callExpression);
                            break;
                        case "Where":
                            StringBuilder sb = new StringBuilder();
                            functions = new List<KeyValuePair<string, Func<int>>>();
                            LambdaExpression lambdaExpression = (LambdaExpression)(((UnaryExpression)callExpression.Arguments[1]).Operand);
                            ExpandExpression(lambdaExpression.Body);

                            PhotoSearch photoSearch = new PhotoSearch();
                            
                            //Put the search logic here
                            for (int i = 0; i < filterList.Count; i++)
                            {
                                object propertyName = filterProperty[i];
                                object symbolName = filterOperator[i];
                                object value = filterList[i];

                                switch ((string)propertyName)
                                {
                                    case "TagName":
                                        photoSearch.PhotoTags = new List<string> { (string)value };
                                        break;
                                    case "Family":
                                        if ((bool)value)
                                            photoSearch.SearchPrivacy = SearchPrivacy.ByFamily;
                                        break;
                                }


                            }
                            photoSearch.User = _service.Token.User;
                            IList<Photo> photoList = new List<Photo>();
                            Photos photos = new Photos(_service, photoSearch);
                            photos.Refresh();
                            _photos = photos.PhotoList;
                            return (IQueryable<TElement>)this;
                        case "OrderBy":
                            LambdaExpression lambdaExpressionOrder = (LambdaExpression)(((UnaryExpression)callExpression.Arguments[1]).Operand);
                            ExpandExpression(lambdaExpressionOrder.Body);
                            return (IQueryable<TElement>)this;
                            break;
                        default:
                            throw new NotImplementedException("This method is not supported");
                    }
                    break;
            }
            return element;
        }

        private void ExpandMemberAccess(MemberExpression m)
        {
            MemberInfo member = m.Member;
            Expression expression = m.Expression;
            switch (expression.NodeType)
            {
                case ExpressionType.Parameter:
                    filterProperty.Add(member.Name);
                    break;
                case ExpressionType.Constant:
                    break;
            }

        }

        private void ExpandConstant(ConstantExpression c)
        {
            object value = c.Value;
            filterList.Add(value);

        }
        public void ExpandExpression(Expression lambdaExpression)
        {
            switch (lambdaExpression.NodeType)
            {
                case ExpressionType.Equal:
                    ExpandBinary((BinaryExpression)lambdaExpression, "=");
                    break;
                case ExpressionType.MemberAccess:
                    ExpandMemberAccess((MemberExpression)lambdaExpression);
                    break;
                case ExpressionType.Constant:
                    ExpandConstant((ConstantExpression)lambdaExpression);
                    break;
                case ExpressionType.AndAlso:
                    ExpandAndAlso((BinaryExpression)lambdaExpression);
                    break;
            }
        }

        void ExpandAndAlso(BinaryExpression binary)
        {
            Expression left = binary.Left;
            Expression right = binary.Right;

            ExpandExpression(left);

            ExpandExpression(right);
        }


        private void ExpandBinary(BinaryExpression binaryExpression, string operatorSymbol)
        {
            ConcatBinary(binaryExpression.Left, binaryExpression.Right, operatorSymbol);
        }


        StringBuilder clauseBuilder = new StringBuilder();

        private void ConcatBinary(Expression left, Expression right, string operatorSymbol)
        {
            ExpandExpression(left);
            filterOperator.Add(operatorSymbol);
            ExpandExpression(right);

            //functions.Add(new KeyValuePair<string, Func<int>>("Tom", Test));
            //filterList.Add( (string)((ConstantExpression)right).Value);
        }

        private int Test()
        {
            //return string.Empty;
            return 0;
        }

        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {
            return CreateQuery<Photo>(expression);
        }

        public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
        {
            MethodCallExpression callExpression = (MethodCallExpression)expression;
            throw new NotImplementedException("No execution is defined");
        }

        public object Execute(System.Linq.Expressions.Expression expression)
        {
            return Execute<Photo>(expression);
        }

        #endregion
    }
}